// Learn cc.Class:
//  - [Chinese] http://www.cocos.com/docs/creator/scripting/class.html
//  - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/class/index.html
// Learn Attribute:
//  - [Chinese] http://www.cocos.com/docs/creator/scripting/reference/attributes.html
//  - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/reference/attributes/index.html
// Learn life-cycle callbacks:
//  - [Chinese] http://www.cocos.com/docs/creator/scripting/life-cycle-callbacks.html
//  - [English] http://www.cocos2d-x.org/docs/editors_and_tools/creator-chapters/scripting/life-cycle-callbacks/index.html
var WebSocket = WebSocket || window.WebSocket || window.MozWebSocket;

var SocketStatus = {
    UNCONNECTED: -1,//未连接
    CONNECTING: 0,  //正在连接
    CONNECTED: 1,   //连接成功
};

cc.Class({
    extends: cc.Component,

    properties: {
        // foo: {
        //     // ATTRIBUTES:
        //     default: null,        // The default value will be used only when the component attaching
        //                           // to a node for the first time
        //     type: cc.SpriteFrame, // optional, default is typeof default
        //     serializable: true,   // optional, default is true
        // },
        // bar: {
        //     get () {
        //         return this._bar;
        //     },
        //     set (value) {
        //         this._bar = value;
        //     }
        // },
        playerUUID: "",
        playerMsgOrder: 0,
        _host: '',
        _socket: null,
        _status: -1,
        _cache: null,
        _scheduler: null,
        sendMsgCache: [],
        rcvMsgListeners: null,
    },

    // LIFE-CYCLE CALLBACKS:
    init: function (host) {
        this._host = host;
        this._status = SocketStatus.CONNECTING;
        this.rcvMsgListeners = {};
        this.connect();
    },


    send: function (msgTbl) {
        // if (msgTbl.m_msgId != gt.CG_HEARTBEAT) {
        //     console.log('发送消息', msgTbl);
        // }
        console.log(msgTbl);
        var packMsgData = msgpack.pack(msgTbl, false);
        var packMsgHead = msgpack.pack(this.getMessageHead(packMsgData), false);
        packMsgData.unshift(0x01);
        var msgEntityLen = packMsgData.length;
        var msgEntityLenArr = [msgEntityLen % 256, Math.floor(msgEntityLen / 256)];
        // 打包消息头
        var msgHeadLen = packMsgHead.length;
        var msgHeadLenArr = [msgHeadLen % 256, Math.floor(msgHeadLen / 256)];
        var msgTotalLen = msgHeadLen + 2 + msgEntityLen + 2; //需要各加两个字节的长度长
        var msgTotalLenHi = Math.floor(msgTotalLen / 256);
        var msgTotalLenLow = msgTotalLen % 256;
        var curTime = Date.now();//moment().unix();
        var time = this.toCByInt(curTime);
        var msgId = this.toCByInt(msgTbl.m_msgId * ((curTime % 10000) + 1));
        var msgEntity = time.concat(msgId, msgHeadLenArr, packMsgHead, msgEntityLenArr, packMsgData);
        var checksum = this.getCheckSum(msgEntity);
        var msgToSend = [msgTotalLenLow, msgTotalLenHi].concat(checksum, msgEntity);
        // 放入到消息缓冲
        var sendingBytes = new Uint8Array(msgToSend);
        this.sendMsgCache.push(sendingBytes);
    },

    queue: function () {
        if (!this._socket) {
            return;
        }

        if (this._status === SocketStatus.UNCONNECTED) {
            return;
        }

        if (this.sendMsgCache.length <= 0) {
            return;
        }

        if (this._socket.readyState == WebSocket.OPEN) {
            console.log('WebSocket is sending...');
            var sendingBuffer = this.sendMsgCache.shift();
            this._socket.send(sendingBuffer);
        }
    },

    connect: function () {
        console.log("WebSocket connecting...");
        this._socket = new WebSocket(this._host);
        this._socket.binaryType = "arraybuffer";

        this._socket.onopen = function (evt) {
            console.log('WebSocket is connected!');
            this._status = SocketStatus.CONNECTED;
            this.schedule(this.queue, 0);
        }.bind(this);

        this._socket.onmessage = function (evt) {
            console.log("WebSocket get typeof->" + (typeof evt.data));
            var msg = msgpack.unpack(evt.data.substr(13));
            if (!msg) {
                return;
            }
            if (msg.m_msgId != 16) {
                console.log('receive msg', msg);
            }
            if (msg.m_errorCode == 0) {
                this.dispatchMessage(msg);
            }
        }.bind(this);

        this._socket.onerror = function (evt) {
            console.log('Websocket onerror...');
            this.unschedule(this.queue);
            this._status = SocketStatus.UNCONNECTED;
        }.bind(this);

        this._socket.onclose = function (evt) {
            console.log('Websocket onclose...');
            this.unschedule(this.queue);
            this._status = SocketStatus.UNCONNECTED;
        }.bind(this);
    },


    close: function () {
        if (this._socket) {
            console.log("Websocket close...");
            this.unschedule(this.queue);
            this._status = SocketStatus.UNCONNECTED;
            this._socket.close();
            this._socket = null;
        }
    },

    getCheckSum: function (time) {
        var crc = this.calcCRC(time, 8);
        return this.toCByShort(crc);
    },

    calcCRC: function (data, length) {
        var sum = 65535;
        for (var i = 0; i < length; i++) {
            var d = data[i];
            sum = this.byteCRC(sum, d);
        }
        return sum;
    },

    byteCRC: function (sum, data) {
        sum = sum ^ data;
        for (var i = 0; i <= 3; i++) {
            if ((sum & 1) == 0) {
                sum /= 2;
            }
            else {
                sum = (sum / 2) ^ 0x70B1;
            }
        }
        return sum;
    },

    toCByShort: function (value) {
        return [value % 256, Math.floor(value / 256)];
    },

    toCByInt: function (value) {
        var b1 = value >> 24;
        var b2 = (value >> 16) % 256;
        var b3 = (value >> 8) % 256;
        var b4 = value % 256;
        return [b4, b3, b2, b1];
    },

    getMessageHead: function (messageEntity) {
        var msgEntityLen = messageEntity.length;
        var beginPos = 0;
        if (msgEntityLen > 0) {
            beginPos = App.util.randomInt(1, msgEntityLen);
        }
        var endPos = beginPos;
        var remainLen = msgEntityLen - beginPos;
        if (remainLen > 0) {
            endPos = beginPos + App.util.randomInt(1, Math.min(128, remainLen));
        }
        var md5str = "";
        if (beginPos > 0 && endPos >= beginPos) {
            var stringMd5 = "";
            for (var i = beginPos - 1; i < endPos; i++) {
                var tmp = parseInt(messageEntity[i]);
                stringMd5 = stringMd5 + App.util.sprintf("%02X", tmp);
            }
            md5str = App.util.hexMd5(stringMd5);
        }
        else {
            beginPos = 0;
            endPos = 0;
        }
        this.playerMsgOrder = this.playerMsgOrder + 1;
        var msgHeadData = {};
        msgHeadData.m_msgId = 500;
        msgHeadData.m_strUserId = 'test1';//sys.params.unionid;
        msgHeadData.m_iMd5Begin = beginPos;
        msgHeadData.m_iMd5End = endPos;
        msgHeadData.m_strMd5 = md5str;
        msgHeadData.m_strVerifyKey = this.playerKeyOnGate;
        msgHeadData.m_lMsgOrder = this.playerMsgOrder;
        return msgHeadData;
    },

    setPlayerUUID: function (playerUUID) {
        this.playerUUID = playerUUID;
    },

    getPlayerUUID: function () {
        return this.playerUUID;
    },

    registerMsgListener: function (msgId, msgTarget, msgFunc) {
        this.rcvMsgListeners[msgId] = [msgTarget, msgFunc];
    },

    unregisterMsgListener: function (msgId) {
        this.rcvMsgListeners[msgId] = null;
    },

    dispatchMessage: function (msgTbl) {
        var rcvMsgListener = this.rcvMsgListeners[msgTbl.m_msgId];
        if (rcvMsgListener) {
            rcvMsgListener[1].call(rcvMsgListener[0], msgTbl);
            return true;
        }
        else {
            return false;
        }
    }
});
